﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;

using Matchingo.Common.Types.Interfaces;
using Matchingo.Common.Types.Types;

namespace Matchingo.Modules.CardGridModule.Views
{
    public partial class CardGridView : UserControl, ICardGridView
    {
        #region ========== Local Members ==========
        
        private static readonly Random RandomGenerator = new Random();

        private CardGridViewModel ViewModel { get; set; }

		#endregion

        #region ========== Constructors ==========

        public CardGridView()
        {
            InitializeComponent();
        }

        #endregion

        #region ========== Public Methods ==========

        public void Initialize(CardGridViewModel viewModel)
        {
            ViewModel = viewModel;
            DataContext = ViewModel;
        }

        public void GridDefinitionUpdated()
        {
            // sanity checks
            if (ViewModel.GameCardSet == null)
            {
                throw new ArgumentException("CardGridView.GridDefinitionUpdated() - sanity checks failed, required fields aren't setup.");
            }

			Dictionary<Guid, Uri> cardGuidToCardImageMapping = SetupCardsAndImages();            

            ClearGameGrid();
            SetupGameGrid();

            // iterate through all the cards and add them to our game grid setting up initial properties, events, etc.
            foreach (Card c in ViewModel.GameCardSet)
            {
                Uri frontUri = cardGuidToCardImageMapping[c.MatchingID];
                BitmapImage frontBmp = new BitmapImage(frontUri);
                Image front = new Image {Source = frontBmp};
                AddCardButton(c, front);
            }
        }

        public Point GetCenterOfCardRelativeToParent(Guid uniqueID)
        {
            Card c = FindCardInGrid(uniqueID);
            if (c == null)
                throw new Exception("GetCenterOfCardRelativeToParent() couldn't find the card requested"); 

            return GetCenterOfCardRelativeToParent(c);
        }

        public Point GetCenterOfCardRelativeToParent(Card c)
        {
            ICardUserControl cardControl = FindCardControlInGrid(c.UniqueID);
            if (cardControl == null)
                throw new Exception("GetCenterOfCardRelativeToParent() couldn't find the card requested"); 

            UserControl uc = (UserControl)cardControl;
            GeneralTransform gt = uc.TransformToVisual(_gameGrid); 
            Point offset = gt.Transform(new Point(0, 0));

            // get the center of the card (should be div 2, but seems more centered with div 4 right now)
            offset.X += _gameGrid.ColumnDefinitions[0].ActualWidth / 2;
            offset.Y += _gameGrid.RowDefinitions[0].ActualHeight;

            return offset;
        }

        #endregion

        #region ========== Private Methods ==========

        private void ClearGameGrid()
        {
            // clear out the buttons
            _gameGrid.Children.Clear();

            _gameGrid.ColumnDefinitions.Clear();
            _gameGrid.RowDefinitions.Clear();                    
        }

		private Dictionary<Guid, Uri> SetupCardsAndImages()
		{
			// sanity check
			if (ViewModel.CardImageUriList.Count < ViewModel.GameCardSet.Count)
				throw new Exception("CardGridView.SetupCardsToImagesMappings() - there are not enough card images to play with the requested number of cards");

			Dictionary<Guid, Uri> cardAndImages = SetupCardsToImagesMappings();

			return cardAndImages;
		}

		private Dictionary<Guid, Uri> SetupCardsToImagesMappings()
        {
			Dictionary<Guid, Uri> cardGuidToCardImageMapping = new Dictionary<Guid, Uri>();

			List<Uri> clonedImagePairs = new List<Uri>(ViewModel.CardImageUriList);            

            foreach (Card c in ViewModel.GameCardSet)
            {
                if (cardGuidToCardImageMapping.Keys.Contains(c.MatchingID)) 
                    continue;

                // get a random seed to read from our image pairs
                int randomX = RandomGenerator.Next(0, clonedImagePairs.Count - 1);        

                // get the next pair of front and rear images for this first card of the pair
                Uri frontUri = clonedImagePairs.ElementAt(randomX);

                cardGuidToCardImageMapping.Add(c.MatchingID, frontUri);

                // remove this image pair so it wont be used again
                clonedImagePairs.Remove(frontUri);
            }

			return cardGuidToCardImageMapping;
        }

        private void SetupGameGrid()
        {
            for (int col = 0; col < ViewModel.GridColumnCount; col++)
            {
                ColumnDefinition colDef = new ColumnDefinition();
                colDef.Width = new GridLength(1, GridUnitType.Star);
                _gameGrid.ColumnDefinitions.Add(colDef);
            }

            for (int row = 0; row < ViewModel.GridRowCount; row++)
            {
                RowDefinition rowDef = new RowDefinition();
                rowDef.Height = new GridLength(1, GridUnitType.Star);
                _gameGrid.RowDefinitions.Add(rowDef);
            }
        }

        private void AddCardButton(Card c, Image front)
        {
            #region Sanity Checks

            // make sure the card passed in wasn't null
            if (c == null)
            {
                throw new ArgumentException("AddCardButton() was passed in a null card");
            }

            #endregion

            // get the row/col this card will go into
            int row, col;
            ConvertIntToRowColumn(_gameGrid.Children.Count, out row, out col);

            // new up a user control using the registered card factory
            UserControl cardControl = ViewModel.GetCardControl(c, front);
            cardControl.SetValue(Grid.RowProperty, row);
            cardControl.SetValue(Grid.ColumnProperty, col);
            cardControl.DataContext = c;
            cardControl.Cursor = Cursors.Hand;
            cardControl.MouseLeftButtonUp += CardControlMouseLeftButtonUp;

            ICardUserControl iCardControl = (ICardUserControl)cardControl;            
            iCardControl.Initialize();

            _gameGrid.Children.Add(cardControl);

            iCardControl.PlayDealingAnimation();            

            c.IsDealt = true;
        }

        private void ConvertIntToRowColumn(int pos, out int row, out int col)
        {
            col = pos % ViewModel.GridColumnCount;
            row = pos / ViewModel.GridColumnCount;
        }

        private ICardUserControl FindCardControlInGrid(Guid uniqueID)
        {
            List<ICardUserControl> cardControls = (from ICardUserControl uc in _gameGrid.Children
                                                   where uc.Card.UniqueID == uniqueID
                                                   select uc).ToList();

            if (cardControls.Count != 1)
                throw new ArgumentException("CardGridView.FindCardControlFromCard() could not find the requested card");

            return cardControls[0];
        }

        private Card FindCardInGrid(Guid uniqueID)
        {
            return FindCardControlInGrid(uniqueID).Card;
        }

        #endregion

        #region ========== Event Handlers ==========

        void CardControlMouseLeftButtonUp(object sender, MouseButtonEventArgs e)
        {
            Guid uniqueID = ((ICardUserControl)sender).Card.UniqueID;
            ViewModel.FlipCard(uniqueID);
        }

        #endregion
    }
}
